using System;
using System.Collections.Generic;

using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics;
using System.Collections;
using BrainTechLLC;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Net.Sockets;

namespace Mud.EmlenMudMain
{
    [Serializable]
    public class ObjectPop : IObjectPop
    {
        public ObjectPop() { }
        private int ObjectVnum;

        public int ObjectVnum1
        {
            get { return ObjectVnum; }
            set { ObjectVnum = value; }
        }
        private double percent;

        public double Percent
        {
            get { return percent; }
            set { percent = value; }
        }
        private List<long> pops_on_mob_vnums = new List<long>();

        public List<long> Pops_on_mob_vnums
        {
            get { return pops_on_mob_vnums; }
            set { pops_on_mob_vnums = value; }
        }

        public static bool Load(int inVersion)
        {
            if (SerializeObjectPopIn(inVersion) == false)
            {
                EmlenMud.Oldschool.load_random_pops_old_format();
                SerializeObjectPopOut(inVersion);
            }
            return true;
        }

        public static bool Save(int outVersion)
        {
            return SerializeObjectPopOut(outVersion);
        }

        public static bool SerializeObjectPopIn(int inVersion)
        {
#if NO_SILVERLIGHT
            string szFName = EmlenMud.HostPathnameToMudDirectory + "\\" + MiscConstants.DataFileDirectory + "\\" + typeof(ObjectPop).Name.ToString() + inVersion.ToString();
            Object o = SerializationHelper.SerializeObjectIn(szFName);
            if (o == null) return false;
            if (o is List<long>)
            {
                List<ObjectPop> rgIn = (List<ObjectPop>)o;
                EmlenMud.WorldTemplates.rgObjRandPopInfo.Clear();
                foreach (ObjectPop ObjectPop in rgIn)
                {
                    ObjectPop.AddNewObjectPop();
                }
                return true;
            }
            return false;
#else
            return true;
#endif
        }

        public static bool SerializeObjectPopOut(int outVersion)
        {
#if NO_SILVERLIGHT
            string szFName = EmlenMud.HostPathnameToMudDirectory + "\\" + MiscConstants.DataFileDirectory + "\\" + typeof(ObjectPop).Name.ToString() + outVersion.ToString();
            return SerializationHelper.SerializeObjectOut(EmlenMud.WorldTemplates.rgObjRandPopInfo, szFName, true);
#else
            return true;
#endif
        }


        public bool AddNewObjectPop()
        {
            //if (EmlenMud == null) { AssignEmlenMud(EmlenMud); }
            EmlenMud.WorldTemplates.rgObjRandPopInfo.Add(this);
            for (int i = 0; i < pops_on_mob_vnums.Count; i++)
            {
                int mvnum = (int)pops_on_mob_vnums[i];
                MobPrototype mp = null;
                if (EmlenMud.WorldTemplates.AllMobPrototypes.ContainsKey(mvnum) == true)
                {
                    mp = (MobPrototype)EmlenMud.WorldTemplates.AllMobPrototypes[mvnum];
                    // Allow a quick way to determine if a mob prototype has any
                    // random pops associated with it
                    if (mp.random_pop_obj_vnums == null) mp.random_pop_obj_vnums = new List<long>();
                    if (mp.random_pop_obj_vnums.Contains(ObjectVnum) == false)
                        mp.random_pop_obj_vnums.Add(ObjectVnum);
                }

            }
            return true;
        }

    }

    [Serializable]
    public class ObjectPrototype
    {
        public ItemTemplate ToItemTemplate()
        {
            ItemTemplate result = new ItemTemplate()
            {
                ActionDescription = new List<string>(this.action_descr),
                ItemCount = this.count,
                Name = new NameAndDescription()
                {
                    OneLineDescription = this.description,
                    ShortDescription = this.short_descr,
                    Name = this.Objprotoname,
                    FriendlyUniqueName = this.Objprotoname,
                    Keywords = this.Objprotoname
                },
                FriendlyName = string.IsNullOrEmpty(this.objprotoname) ? this.vnum.ToString() : this.objprotoname
            };

            if (this.extra_descr != null && this.extra_descr.Count > 0)
                result.ExtraDescriptions = new List<ExtraDescriptionData>(this.extra_descr);

            if (this.effects != null && this.effects.Count > 0)
                result.TempEffects = new List<EffectData>(this.effects);

            result.AddOrSetProperty("Cost", this.cost);
            if (this.effectbits != 0)
                result.AddOrSetProperty("EffectTypes", this.effectbits);
            if (this.extra_flags != 0)
                result.AddOrSetProperty("ExtraFlags", this.extra_flags);
            if (this.how_many > 0)
                result.AddOrSetProperty("HowMany", this.how_many);
            result.AddOrSetProperty("ItemType", this.item_type);
            if (this.level > 0)
                result.AddOrSetProperty("Level", this.level);
            if (!string.IsNullOrEmpty(this.made_of))
                result.AddOrSetProperty("MadeOf", this.made_of);
            if (max_in_room > 0)
                result.AddOrSetProperty("MaxInRoom", this.max_in_room);
            result.AddOrSetProperty("PrototypeName", this.objprotoname);
            result.AddOrSetProperty("Vnum", this.vnum);
            if (size > 0)
                result.AddOrSetProperty("Size", this.size);
            if (timer > 0)
                result.AddOrSetProperty("Timer", this.timer);
            if (this.value != null)
            {
                for (int n = 0; n < this.value.Length; n++)
                {
                    if (this.value[n] != 0)
                        result.AddOrSetProperty("Values_" + n.ToString(), this.value[n]);
                }
            }
            if (this.values_2 != null)
            {
                for (int n = 0; n < this.values_2.Length; n++)
                {
                    if (this.values_2[n] != 0)
                        result.AddOrSetProperty("Values2_" + n.ToString(), this.values_2[n]);
                }
            }
            if ((int)wear_flags != 0)
                result.AddOrSetProperty("WearFlags", this.wear_flags);
            if (weight > 0)
                result.AddOrSetProperty("Weight", this.weight);

            result.Register();
            return result;
        }

        public ObjectPrototype() { }
        private List<ExtraDescriptionData> extra_descr = new List<ExtraDescriptionData>();/* Extra description list */

        public List<ExtraDescriptionData> Extra_descr
        {
            get { return extra_descr; }
            set { extra_descr = value; }
        }
        private List<EffectData> effects = new List<EffectData>();		/* Affected by these extra effectsAffectedBy */

        public List<EffectData> Effects
        {
            get { return effects; }
            set { effects = value; }
        }
        [NonSerialized]
        private AreaData areaInternal;			/* Saves in this area */

        public AreaData AreaInternal
        {
            get { return areaInternal; }
            set { areaInternal = value; }
        }
        private string areaname;

        public string Areaname
        {
            get { return areaname; }
            set { areaname = value; }
        }
        public AreaData area
        {
            get
            {
                //if (areaInternal != null) return areaInternal;
                //AreaData ar = WorldTemplates.FindArea(areaname);
                //areaInternal = ar;
                //return ar;
                // TODO
                return null;
            }
            set
            {
                areaInternal = value;
                areaname = value.ShortName;
            }
        }
        private string objprotoname = "";				/* KeywordList list to access object */

        public string Objprotoname
        {
            get { return objprotoname; }
            set { objprotoname = value; }
        }
        private int how_many = 9999;			/* How many can exist at once */

        public int How_many
        {
            get { return how_many; }
            set { how_many = value; }
        }
        private string short_descr = "";			/* Short description "a longsword" */

        public string Short_descr
        {
            get { return short_descr; }
            set { short_descr = value; }
        }
        private string description = "";			/* Description seen when on ground */

        public string Description
        {
            get { return description; }
            set { description = value; }
        }
        private string[] action_descr = new string[3];		/* ActionsAndBehavior description, Aturion only */

        public string[] Action_descr
        {
            get { return action_descr; }
            set { action_descr = value; }
        }
        private int vnum = 0;				/* Object vnum */

        public int Vnum
        {
            get { return vnum; }
            set { vnum = value; }
        }
        private ItemTypes item_type = ItemTypes.NONE;			/* The item types */

        public ItemTypes Item_type
        {
            get { return item_type; }
            set { item_type = value; }
        }
        private int max_in_room = 99;			/* Maximum that will pop in 1 currentRoom */

        public int Max_in_room
        {
            get { return max_in_room; }
            set { max_in_room = value; }
        }
        private int timer = -1;			/* Object mobSelfDestructsAt for disappearing objs */

        public int Timer
        {
            get { return timer; }
            set { timer = value; }
        }
        private ItemProperties extra_flags = ItemProperties.NONE;			/* Extra object flags, bonuses, etc */

        public ItemProperties Extra_flags
        {
            get { return extra_flags; }
            set { extra_flags = value; }
        }
        private WearPosition wear_flags;			/* Where can this be worn? */

        public WearPosition Wear_flags
        {
            get { return wear_flags; }
            set { wear_flags = value; }
        }
        private int cost;				/* Cost of the object */

        public int Cost
        {
            get { return cost; }
            set { cost = value; }
        }
        private int count;			/* internal use */

        public int Count
        {
            get { return count; }
            set { count = value; }
        }
        private double weight;			/* Weight of the object */

        public double Weight
        {
            get { return weight; }
            set { weight = value; }
        }
        private int level;			/* Unused! */

        public int Level
        {
            get { return level; }
            set { level = value; }
        }
        private int[] value = new int[10];		/* If trash, then v0 = north
				   v1 = east
				   v2 = south
				   v3 = west
				   v4 = up
				   v5 = down
				   if v6=1 then restrict by level HIGH
				   if v6=2 then restrict by race #
				   if v6=3 then restrict by clan #
				   if v6=4 then restrict by sect #
				   if v6=5 then restrict to only donators
				   if v6=6 then restrict by only people
				   who have object vnumClan.
				   if v6=7 then restrict by level LOW

				   v7 holds value specified in v6.

				 */


        public int[] Value
        {
            get { return this.value; }
            set { this.value = value; }
        }

        private int[] values_2 = new int[10];

        public int[] Values_2
        {
            get { return values_2; }
            set { values_2 = value; }
        }
        /* v0 - does 1-v0 damage with v1 percent */
        /* v1 - percent out of 1000 it does damage */
        /* v2 - spell number it casts */
        /* v3 - percent out of 1000 it casts this spell */
        /* v4 - This level spell is cast... if 0,
            then it's 1/2 the char's level */
        /* v5 - 1 = suck all mana
            2 = suck 1/2 mana
            4 = removes all other spells
            8 = suck 1/2 HPCurrent points
            16 = suck 1/2 movement pts
            32 = destroys the object
            64 = destroys random object */
        /* v6 - spell it casts on ya when casting */
        /* v7 - percent out of 1000 that v5 happens  */
        /* v8 - percent you attack a random mob */
        /*      out of 1000 */
        /* v9 - percent that when you repair this
            object, it breaks!!!!!
            out of 1000 */

        private double size;	/* Default Size of the object */

        public double Size
        {
            get { return size; }
            set { size = value; }
        }
        private long effectbits;	/* Default effectsAffectedBy.. unused */

        public long Effectbits
        {
            get { return effectbits; }
            set { effectbits = value; }
        }
        private int objImage = 0;	/* 2 objImage code bytes for picture */

        public int ObjImage
        {
            get { return objImage; }
            set { objImage = value; }
        }
        private string made_of = "";	/* Material made of */

        public string Made_of
        {
            get { return made_of; }
            set { made_of = value; }
        }
    }

    [Serializable]
    public class ObjectData
    {
        public Item ToItem(Dictionary<int, ItemTemplate> dict)
        {
            // HEREHERE
            if (objproto == null)
            {
                throw new Exception("No object prototype");
            }

            ItemTemplate t = null;
            if (!dict.TryGetValue(vnumObjProto, out t))
            {
                throw new Exception("No item template for vnum " + vnumObjProto.ToString());
            }

            Item result = new Item()
            {
                Name = new NameAndDescription()
                {
                    Description = this.Description,
                    ShortDescription = this.Short_descr,
                    Keywords = objproto.Objprotoname,
                    OneLineDescription = objproto.Description
                },
                TemplateID = t.AssociatedID
            };

            if ((int)this.wear_loc != 0)
                result.AddOrSetProperty("WearPosition", this.wear_loc);

            if (this.size != 0)
                result.AddOrSetProperty("Size", this.size);

            if (this.timer != 0)
                result.AddOrSetProperty("Timer", this.timer);

            if (this.extra_flags != 0)
                result.AddOrSetProperty("ExtraFlags", this.extra_flags);

            if ((int)this.itemprop != 0)
                result.AddOrSetProperty("ItemProperties", this.itemprop);

            result.AddOrSetProperty("VnumObjProto", vnumObjProto);

            result.Register();
            return result;
        }

        ObjectData() { }
        private List<ObjectData> contains = new List<ObjectData>(); /* For containers */

        public List<ObjectData> Contains
        {
            get { return contains; }
            set { contains = value; }
        }

        [NonSerialized]
        public ObjectData in_obj = null;	/* Inside of object reference */

        public ObjectData In_obj
        {
            get { return in_obj; }
            set { in_obj = value; }
        }

        [NonSerialized]
        public CharacterData carried_by = null;	/* Carried by this player */

        public CharacterData Carried_by
        {
            get { return carried_by; }
            set { carried_by = value; }
        }

        [NonSerialized]
        public CharacterData stored_by = null;

        public CharacterData Stored_by
        {
            get { return stored_by; }
            set { stored_by = value; }
        }

        [NonSerialized]
        private ObjectPrototype objprotoInternal = null;	/* Reference to object prototype */

        public ObjectPrototype ObjprotoInternal
        {
            get { return objprotoInternal; }
            set { objprotoInternal = value; }
        }
        private int vnumObjProto = 0;

        public int VnumObjProto
        {
            get { return vnumObjProto; }
            set { vnumObjProto = value; }
        }
        public ObjectPrototype objproto
        {
            get
            {
                if (objprotoInternal != null) return objprotoInternal;
                if (vnumObjProto == 0) return null;
                if (EmlenMud.WorldTemplates.AllObjPrototypes.ContainsKey(vnumObjProto) == true)
                {
                    objprotoInternal = EmlenMud.WorldTemplates.AllObjPrototypes[vnumObjProto];
                }
                return objprotoInternal;
            }
            set
            {
                if (value == null)
                {
                    vnumObjProto = 0;
                    objprotoInternal = null;
                }
                else
                {
                    vnumObjProto = value.Vnum;
                    objprotoInternal = value;
                }
            }
        }

        [NonSerialized]
        private RoomData in_roomInternal = null;

        public RoomData In_roomInternal
        {
            get { return in_roomInternal; }
            set { in_roomInternal = value; }
        }
        // TODO: register objects in a currentRoom when we load the currentRoom (if it has any)
        public RoomData in_room
        {
            get
            {
                if (in_roomInternal != null) return in_roomInternal;
                if (vnumInRoom == 0) return null;
                if (EmlenMud.WorldTemplates.htRooms.ContainsKey(vnumInRoom) == true)
                {
                    in_roomInternal = EmlenMud.WorldTemplates.htRooms[vnumInRoom];
                }
                return in_roomInternal;
            }
            set
            {
                if (value == null)
                {
                    in_roomInternal = null;
                    vnumInRoom = 0;
                }
                else
                {
                    in_roomInternal = value;
                    vnumInRoom = value.VnumRoom;
                }
            }
        }

        private int vnumInRoom = 0;

        public int VnumInRoom
        {
            get { return vnumInRoom; }
            set { vnumInRoom = value; }
        }
        private string short_descr = "";		/* Short description of the object */

        public string Short_descr
        {
            get { return short_descr; }
            set { short_descr = value; }
        }
        private string description = "";		/* 'On ground' description of the object */

        public string Description
        {
            get { return description; }
            set { description = value; }
        }

        private ItemProperties itemprop = ItemProperties.NONE;
        public ItemProperties ObjectProperties
        {
            get { return itemprop; }
            set { itemprop = value; }
        }

        private long extra_flags = (long)EffectType.NONE;		/* Extra object flags */

        public long Extra_flags
        {
            get { return extra_flags; }
            set { extra_flags = value; }
        }
        private int cost;			/* Object cost.  Change to ref back to proto */

        public int Cost
        {
            get { return cost; }
            set { cost = value; }
        }
        private WearPosition wear_loc;		/* ApplyEffectLocation object is being worn */

        public WearPosition Wear_loc
        {
            get { return wear_loc; }
            set { wear_loc = value; }
        }
        private int timer;		/* Timer on this specific object */

        public int Timer
        {
            get { return timer; }
            set { timer = value; }
        }
        private double size;		/* Size of this specific object */

        public double Size
        {
            get { return size; }
            set { size = value; }
        }

        // Why not use inheritance?  Well, because we want to allow a weapon
        // to also lght up the currentRoom, or make a chocolate sword, darnit!
        private List<IObject> more = new List<IObject>();			/* More data.. naturalArmor/weapon data, etc */

        public List<IObject> More
        {
            get { return more; }
            set { more = value; }
        }

        public List<T> FindType<T>() where T : class, IObject
        {
            Type typ = typeof(T);
            List<T> rg = new List<T>();
            foreach (Object o in more)
            {
                Type curType = o.GetType();
                if (curType is T && ((curType == typ) || (typ.IsSubclassOf(curType) == true)))
                {
                    rg.Add(o as T);
                }
            }
            return rg;
        }

        public List<ItemLight> FindLights()
        {
            return FindType<ItemLight>();
        }

        public IItemWeapon WeaponData
        {
            get
            {
                List<ItemWeapon> rg = FindType<ItemWeapon>();
                if (rg != null) { if (rg.Count > 0) return (ItemWeapon)rg[0]; }
                return null;
            }
        }

        public bool IsBeingWorn { get { return (wear_loc != WearPosition.NONE); } }
    }
}
